package com.ec.edu.common.hello;

import com.ec.edu.common.constants.NettyConstant;
import com.ec.edu.common.enums.MsgType;
import com.ec.edu.common.decoder.MsgPackDecoder;
import com.ec.edu.common.encoder.MsgPackEncoder;
import com.ec.edu.common.constants.NettyMap;
import com.ec.edu.common.vo.DeviceParamsVo;
import com.ec.edu.common.vo.MessageVo;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.HashedWheelTimer;
import lombok.extern.slf4j.Slf4j;

import java.util.Scanner;
import java.util.concurrent.TimeUnit;

@Slf4j
public class HelloNettyClient implements Runnable {

    private static String clientId = "100001";
    static ClientHandler client = null;

    public static SocketChannel socketChannel;

    int timeout = 10;
    protected final HashedWheelTimer timer = new HashedWheelTimer(timeout, TimeUnit.SECONDS);

    public static void start(String clientId) throws InterruptedException {
        HelloNettyClient.clientId = clientId;
        client = new ClientHandler(clientId);

        new Thread(new HelloNettyClient()).start();

        @SuppressWarnings("resource")
        Scanner scanner = new Scanner(System.in);
        //hello:1
        //hello:2
        while(client.sendMsg(scanner.nextLine()));
    }

    @Override
    public void run() {
        String host = NettyConstant.host;
        int port = NettyConstant.port;

        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(workerGroup);
            b.channel(NioSocketChannel.class);
            b.option(ChannelOption.SO_KEEPALIVE, true);
            b.handler(new LoggingHandler(LogLevel.INFO));

            ConnectionWatchdog watchdog = new ConnectionWatchdog(b, timer, port,host, true) {
                @Override
                public ChannelHandler[] handlers() {
                    return new ChannelHandler[]{
                            this,
                            new IdleStateHandler(
                                    0, 0, 5, TimeUnit.SECONDS),
                            new LengthFieldBasedFrameDecoder(65535, 0,2,0,2),
                            new MsgPackDecoder(),
                            new LengthFieldPrepender(2),
                            new MsgPackEncoder(),
                            client
                    };
                }
            };

            ChannelFuture future = null;
            try {
                synchronized (b) {
                    b.remoteAddress(host,port);
                    b.handler(new ChannelInitializer<Channel>() {
                        //初始化channel
                        @Override
                        protected void initChannel(Channel ch) throws Exception {
                            ch.pipeline().addLast(watchdog.handlers());
                        }
                    });
                    //启动客户端
                    future =  b.connect(host,port);
                }
                // 以下代码在synchronized同步块外面是安全的
                future.sync();

                if(future.isSuccess()) {
                    socketChannel = (SocketChannel) future.channel();
                    NettyMap.channelMap.put(clientId, socketChannel.id());
                    NettyMap.channels.add(socketChannel);

                    log.info("客户端完成启动-------------,ip为{},端口为{}",host,port);
                    MessageVo message = new MessageVo();
                    DeviceParamsVo deviceParams = new DeviceParamsVo();
                    deviceParams.setSimIccid("1233456678789");

                    message.setMsgType(MsgType.DEVICE.getMsgType());
                    message.setClientId(clientId);
                    message.setData(deviceParams);
                    client.sendMsg(message);
                }
            } catch (Throwable t) {
                log.error("客户端连接失败------------,ip为{},端口为{}",host,port);
                log.error("异常：", t);
                if (null != future) {
                    timer.newTimeout(watchdog, timeout, TimeUnit.SECONDS);
                }
            }
        } catch (Exception e) {
            log.error("异常：", e);
        } finally {
            //netty优雅退出机制，这里会影响到上面定时任务的执行，所以不退出
//            workerGroup.shutdownGracefully();
        }
    }
}
